package com.qumitech.visualnovel.service.novel;

import com.qumitech.visualnovel.model.dao.console.BookReadInfoDAOV2;
import com.qumitech.visualnovel.model.dao.console.ChapterInfoDAO;
import com.qumitech.visualnovel.model.dao.console.OptionInfoDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.io.*;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;

import com.qumitech.visualnovel.component.config.Config;
import com.qumitech.visualnovel.model.common.BaseResponse;
import com.qumitech.visualnovel.model.dao.console.BookReadInfoDAO;
import com.qumitech.visualnovel.datasource.console.BookReadInfoDAOMapper;
import com.qumitech.visualnovel.model.dao.app.*;
import com.qumitech.visualnovel.model.dao.editor.*;
import com.qumitech.visualnovel.datasource.app.*;
import com.qumitech.visualnovel.datasource.editor.*;
import com.qumitech.visualnovel.model.dto.*;

@Slf4j
@Service
public class BookServiceImpl implements BookService {
    static final String DATA_PATH = Config.getProperty("app.dataPath");
    static final String SCRIPT_RUNNER = Config.getProperty("app.scriptRunner");

    @Autowired
    BookDAOMapper bookDAOMapper;

    @Autowired
    ChapterDAOMapper chapterDAOMapper;

    @Autowired
    FrameDAOMapper frameDAOMapper;

    @Autowired
    BookInfoDAOMapper bookInfoDAOMapper;

    @Autowired
    BookRecommendDAOMapper bookRecommendDAOMapper;

    @Autowired
    BookCommentDAOMapper bookCommentDAOMapper;

    @Autowired
    BookClassifyConfDAOMapper bookClassifyConfDAOMapper;

    @Autowired
    BookClassifyRecordDAOMapper bookClassifyRecordDAOMapper;

    @Autowired
    BookReadInfoDAOMapper bookReadInfoDAOMapper;

    private void setBookChaptersFee(Long bookId, Integer freeChapters, String discountRate) {
        int totalChapters = chapterDAOMapper.totalChapters(bookId);
        for (int idx = 1; idx <= totalChapters; ++idx) {
            ChapterDAO chapterDAO = chapterDAOMapper.getChapter(bookId, idx);
            if (chapterDAO != null) {
                chapterDAO.setFee(chapterDAO.getWordsNum() / 100);
                if (freeChapters != null && idx <= freeChapters) {
                    chapterDAO.setFee(0);
                }
                chapterDAOMapper.updateByPrimaryKeySelective(chapterDAO);
            }
        }
    }

    private void publishTextNovel(Long bookId) {
        try {
            String strBookId = String.valueOf(bookId);
            log.info("publish text book task: bookId={}, indexList={}", strBookId, "ALL");
            List<String> commands = new ArrayList<>(Arrays.asList("bash", SCRIPT_RUNNER, "publish", strBookId, "ALL"));
            Process process = new ProcessBuilder(commands).redirectErrorStream(true).start();
        } catch (IOException e) {
            log.error("failed to execute publish", e);
        }
    }

    @Override
    public BaseResponse update(List<BookUpdateDTO> body) {
        for (BookUpdateDTO item : body) {
            BookDAO record = new BookDAO();
            record.setId(item.getId());
            record.setTitle(item.getTitle());
            record.setCover(item.getCover());
            record.setCoverWithWords(item.getCoverWithWords());
            record.setComments(item.getComments());
            record.setLabels(item.getLabels());
            if (item.getDiscountRate() != null)
                record.setDiscountRate(item.getDiscountRate().trim());
            record.setDiscountStartAt(item.getDiscountStartAt());
            record.setDiscountEndAt(item.getDiscountEndAt());
            record.setDelete(item.getDelete());
            bookDAOMapper.updateByPrimaryKeySelective(record);

            if (item.getDiscountRate() != null) {
                this.setBookChaptersFee(record.getId(), item.getFreeChapters(), record.getDiscountRate());
            }
        }
        return new BaseResponse();
    }

    @Override
    public BaseResponse getBookRating(Long bookId) {
        BookInfoDAO bookInfoDAO = bookInfoDAOMapper.selectRecord(bookId);

        JSONObject result = new JSONObject();
        if (bookInfoDAO != null) {
            result.put("book_id", bookInfoDAO.getBookId());
            result.put("level", bookInfoDAO.getRatingLevel());
            result.put("rating", bookInfoDAO.getRatingInfo());
        }
        return new BaseResponse(result);
    }

    @Override
    public BaseResponse updateBookRating(BookRatingDTO body) {
        BookInfoDAO bookInfoDAO = bookInfoDAOMapper.selectRecord(body.getBookId());
        if (bookInfoDAO != null) {
            bookInfoDAO.setBookId(body.getBookId());
            bookInfoDAO.setRatingLevel(body.getLevel());
            bookInfoDAO.setRatingInfo(body.getRating());
            bookInfoDAOMapper.updateByPrimaryKeySelective(bookInfoDAO);
        } else {
            bookInfoDAO = new BookInfoDAO();
            bookInfoDAO.setBookId(body.getBookId());
            bookInfoDAO.setRatingLevel(body.getLevel());
            bookInfoDAO.setRatingInfo(body.getRating());
            bookInfoDAOMapper.insertSelective(bookInfoDAO);
        }
        return new BaseResponse();
    }

    @Override
    public BaseResponse list(BookQueryDTO param) {
        int total = bookDAOMapper.total(param);
        List<BookDAO> list = bookDAOMapper.list(param);

        JSONArray result = new JSONArray();
        for (BookDAO item : list) {
            JSONObject jItem = new JSONObject();
            jItem.put("bookId", item.getId());
            jItem.put("title", item.getTitle());
            jItem.put("cover", item.getCover());
            jItem.put("cover_with_words", item.getCoverWithWords());
            jItem.put("publishState", item.getPublishState());
            jItem.put("author", bookDAOMapper.getAuthorName(item.getId()));
            jItem.put("chaptersNum", chapterDAOMapper.totalChapters(item.getId()));
            jItem.put("freeChapters", chapterDAOMapper.freeChapters(item.getId()));
            jItem.put("category", item.getCategory());
            jItem.put("publishUpdateAt", item.getPublishUpdateAt());
            jItem.put("updatedAt", item.getUpdatedAt());
            jItem.put("totalWords", chapterDAOMapper.totalWords(item.getId()));
            jItem.put("totalPrice", chapterDAOMapper.totalPrice(item.getId()));
            jItem.put("discountRate", item.getDiscountRate());
            jItem.put("discountStartAt", item.getDiscountStartAt());
            jItem.put("discountEndAt", item.getDiscountEndAt());
            jItem.put("delete",item.getDelete());
            result.add(jItem);
        }
        return new BaseResponse(result, total);
    }

    @Override
    public BaseResponse preview(BookPreviewDTO param) {
        Long bookId = param.getBookId();
        Integer chapterIndex = param.getChapterIndex();
        Long frameId = param.getFrameId();

        if (bookId == null) {
            return new BaseResponse(-1, "书籍不存在");
        }

        String url = "index.html?bookId=" + bookId + "&chapterIndex=" + chapterIndex;
        if (frameId != null) url += "&frameId=" + frameId;

        try {
            String strBookId = String.valueOf(bookId);
            List<String> commands = new ArrayList<>(Arrays.asList("bash", SCRIPT_RUNNER, "preview", strBookId));
            Process process = new ProcessBuilder(commands).redirectErrorStream(true).start();
        } catch (IOException e) {
            log.error("failed to execute preview", e);
        }

        JSONObject result = new JSONObject();
        result.put("url", url);
        return new BaseResponse(result);
    }

    @Override
    public BaseResponse addBookRecommend(BookRecommendAddDTO param) {
        BookRecommendDAO bookRecommendDAO = new BookRecommendDAO();
        bookRecommendDAO.setBookId(param.getBookId());
        bookRecommendDAO.setBgImg(param.getBgImg());
        bookRecommendDAO.setPosition(param.getPosition());
        bookRecommendDAO.setOrder(param.getOrder());
        bookRecommendDAO.setLabels(param.getLabels());
        bookRecommendDAO.setStart(param.getStart());
        bookRecommendDAO.setEnd(param.getEnd());
        bookRecommendDAOMapper.insertSelective(bookRecommendDAO);
        return new BaseResponse();
    }

    @Override
    public BaseResponse delBookRecommend(Long id) {
        BookRecommendDAO bookRecommendDAO = bookRecommendDAOMapper.selectByPrimaryKey(id);
        if (bookRecommendDAO != null) {
            bookRecommendDAO.setIfDelete(true);
            bookRecommendDAOMapper.updateByPrimaryKeySelective(bookRecommendDAO);
        }
        return new BaseResponse();
    }

    @Override
    public BaseResponse updateBookRecommend(Long id, BookRecommendAddDTO param) {
        BookRecommendDAO bookRecommendDAO = bookRecommendDAOMapper.selectByPrimaryKey(id);
        if (bookRecommendDAO != null) {
            bookRecommendDAO.setBookId(param.getBookId());
            bookRecommendDAO.setBgImg(param.getBgImg());
            bookRecommendDAO.setPosition(param.getPosition());
            bookRecommendDAO.setOrder(param.getOrder());
            bookRecommendDAO.setLabels(param.getLabels());
            bookRecommendDAO.setStart(param.getStart());
            bookRecommendDAO.setEnd(param.getEnd());
            bookRecommendDAOMapper.updateByPrimaryKeySelective(bookRecommendDAO);
        }
        return new BaseResponse();
    }

    @Override
    public BaseResponse getBookRecommend(BookRecommendQueryDTO param) {
        int total = bookRecommendDAOMapper.total(param);
        List<BookRecommendDAO> list = bookRecommendDAOMapper.list(param);

        JSONObject result = new JSONObject();
        result.put("total", total);
        result.put("list", new JSONArray());

        Date nowTime = new Date();
        list.forEach(item -> {
            BookDAO bookDAO = bookDAOMapper.selectByPrimaryKey(item.getBookId());
            JSONObject jItem = new JSONObject();
            jItem.put("id", item.getId());
            jItem.put("bgImg",item.getBgImg());
            jItem.put("bookId", item.getBookId());
            jItem.put("title", bookDAO.getTitle());
            jItem.put("position", item.getPosition());
            jItem.put("order", item.getOrder());
            jItem.put("labels", item.getLabels());

            Date startTime = item.getStart();
            Date endTime = item.getEnd();
            jItem.put("start", startTime);
            jItem.put("end", endTime);
            if (startTime != null && nowTime.before(startTime)) {
                jItem.put("status", "Waiting");
            } else if (startTime != null && endTime != null && nowTime.compareTo(startTime) >= 0 && nowTime.compareTo(endTime) <= 0) {
                jItem.put("status", "Online");
            } else {
                jItem.put("status", "Offline");
            }
            result.getJSONArray("list").add(jItem);
        });

        return new BaseResponse(result);
    }

    @Override
    public BaseResponse getBookNameOption(List<String> publishStates, String category) {
        List<Map<Long, String>> options = bookDAOMapper.getBookNameOption(publishStates, category);
        return new BaseResponse(options);
    }

    @Override
    public BaseResponse getBookComment(BookCommentQueryDTO param) {
        int total = bookCommentDAOMapper.total(param);
        List<BookCommentDAO> list = bookCommentDAOMapper.list(param);

        Set<Long> bookIds = new HashSet<>();
        list.forEach(item -> {
            bookIds.add(item.getBookId());
        });
        Map<Long, BookDAO> bookInfoMap = bookIds.isEmpty() ? new HashMap<>() : bookDAOMapper.getBookMapByIds(bookIds);

        JSONArray result = new JSONArray();
        list.forEach(item -> {
            JSONObject jItem = new JSONObject();
            jItem.put("commentId", item.getCommentId());
            jItem.put("uid", item.getUid());
            jItem.put("bookId", item.getBookId());
            jItem.put("likeNum", item.getLikeNum());
            jItem.put("comment", item.getComment());
            jItem.put("createdAt", item.getCreatedAt());

            BookDAO bookDAO = bookInfoMap.get(item.getBookId());
            jItem.put("title", bookDAO == null ? null : bookDAO.getTitle());

            result.add(jItem);
        });

        return new BaseResponse(result, total);
    }

    @Override
    public BaseResponse textNovelImport(HttpServletRequest request) {
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        MultipartFile originFile = multipartRequest.getFile("file");

        File textnovelDir = new File(DATA_PATH + "textnovel");
        if (!textnovelDir.exists()) {
            textnovelDir.mkdirs();
        }

        try {
            String originalName = originFile.getOriginalFilename();
            String saveFilePath = String.format(DATA_PATH + "textnovel/" + originalName);
            File saveFile = new File(saveFilePath);
            originFile.transferTo(saveFile);
        } catch (Exception e) {
            e.printStackTrace();
            String originalName = originFile.getOriginalFilename();
            return new BaseResponse(-1, "输入参数错误: " + originalName);
        }

        String checkResult = null;
        try {
            String originalName = originFile.getOriginalFilename();
            String saveFilePath = String.format(DATA_PATH + "textnovel/" + originalName);
            List<String> commands = new ArrayList<>(Arrays.asList("bash", SCRIPT_RUNNER, "textnovel", saveFilePath));
            // List<String> commands = new ArrayList<>(Arrays.asList("python3", SCRIPT_RUNNER, "textnovel", saveFilePath));
            Process process = new ProcessBuilder(commands).redirectErrorStream(true).start();

            InputStream inputStream = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "utf8"));
            StringBuilder builder = new StringBuilder();
            String line = null;
            while (null != (line = reader.readLine())) {
                builder.append(line);
            }
            checkResult = builder.toString();
        } catch (IOException e) {
            e.printStackTrace();
            String originalName = originFile.getOriginalFilename();
            return new BaseResponse(-1, "执行失败: ", originalName);
        }

        int startIndex = checkResult.indexOf("@@@@");
        if (startIndex >= 0) {
            int endIndex = checkResult.lastIndexOf("@@@@");
            if (endIndex >= 0) {
                JSONObject result = JSONObject.parseObject(checkResult.substring(startIndex + 4, endIndex));
                return new BaseResponse(0, "导入成功", result);
            }
        }

        BaseResponse result = new BaseResponse(-1, "导入失败");
        result.put("data", checkResult == null ? "未知原因" : checkResult);
        return result;
    }

    @Override
    public BaseResponse textNovelOnline(Boolean ifOnline, List<Long> bookIdList) {
        if (ifOnline && bookIdList.size() >= 5) {
            return new BaseResponse(-1, "批量上架书籍不能超过5本");
        }

        String publishState = ifOnline ? "Finished" : "Unpublished";
        for (Long bookId : bookIdList) {
            if (ifOnline) {
                this.publishTextNovel(bookId);
            }

            BookDAO record = new BookDAO();
            record.setId(bookId);
            record.setPublishState(publishState);
            bookDAOMapper.updateByPrimaryKeySelective(record);
        }
        return new BaseResponse();
    }

    @Override
    public BaseResponse getChapterFrames(Long chapterId) {
        ChapterDAO chapter = chapterDAOMapper.selectByPrimaryKey(chapterId);
        if (chapter == null) {
            return new BaseResponse(-1, "chapter not exists");
        }

        JSONArray result = new JSONArray();
        List<FrameDAO> frames = frameDAOMapper.getAllFrames(chapter.getBookId(), chapter.getIndex(),chapter.getVersion());
        for (FrameDAO item : frames) {
            JSONObject jItem = new JSONObject();
            jItem.put("frameId", item.getId());
            jItem.put("content", item.getContent());
            result.add(jItem);
        }

        return new BaseResponse(result);
    }

    @Override
    public BaseResponse updateChapterFrames(Long chapterId, List<BookFrameUpdateDTO> body) {
        ChapterDAO chapter = chapterDAOMapper.selectByPrimaryKey(chapterId);
        if (chapter == null) {
            return new BaseResponse(-1, "chapter not exists");
        }

        Integer totalChangeNum = 0;
        for (BookFrameUpdateDTO item : body) {
            FrameDAO frame = new FrameDAO();
            frame.setId(item.getFrameId());
            frame.setContent(item.getContent());
            frameDAOMapper.updateByPrimaryKeySelective(frame);

            if (item.getWordsChangeNum() != null) {
                totalChangeNum += item.getWordsChangeNum();
            }
        }

        if (!totalChangeNum.equals(0)) {
            chapter.setWordsNum(chapter.getWordsNum() + totalChangeNum);
            chapterDAOMapper.updateByPrimaryKeySelective(chapter);
        }

        return new BaseResponse();
    }

    @Override
    public BaseResponse addBookClissfyConf(BookClissfyConfDTO body) {
        if (body.getLabels() == null) {
            return new BaseResponse(-1, "标签不能为空");
        }

        BookClassifyConfDAO record = bookClassifyConfDAOMapper.selectByLabels(body.getLabels());
        if (record != null) {
            return new BaseResponse(-1, "标签已存在");
        }

        BookClassifyConfDAO conf = new BookClassifyConfDAO();
        conf.setOrder(body.getOrder());
        conf.setIcon(body.getIcon());
        conf.setLabels(body.getLabels());
        conf.setIfRecommend(body.getIfRecommend());
        conf.setStatus(body.getStatus());
        conf.setClassifyType(body.getClassifyType());
        conf.setDisplayType(body.getDisplayType());
        bookClassifyConfDAOMapper.insertSelective(conf);
        return new BaseResponse();
    }

    @Override
    public BaseResponse delBookClissfyConf(Long classifyId) {
        bookClassifyConfDAOMapper.deleteByPrimaryKey(classifyId);
        return new BaseResponse();
    }

    @Override
    public BaseResponse updateBookClissfyConf(List<BookClissfyConfUpdateDTO> body) {
        for (BookClissfyConfUpdateDTO elemt : body) {
            if (elemt.getLabels() == null) {
                continue;
            }
            BookClassifyConfDAO record = bookClassifyConfDAOMapper.selectByLabels(elemt.getLabels());
            if (record != null) {
                return new BaseResponse(-1, "标签已存在");
            }
        }

        for (BookClissfyConfUpdateDTO elemt : body) {
            BookClassifyConfDAO conf = new BookClassifyConfDAO();
            conf.setId(elemt.getClassifyId());
            conf.setOrder(elemt.getOrder());
            conf.setIcon(elemt.getIcon());
            conf.setLabels(elemt.getLabels());
            conf.setStatus(elemt.getStatus());
            bookClassifyConfDAOMapper.updateByPrimaryKeySelective(conf);
        }

        return new BaseResponse();
    }

    @Override
    public BaseResponse getBookClissfyRecord(Boolean ifRecommend, Integer classifyType) {
        List<BookClassifyConfDAO> confs = bookClassifyConfDAOMapper.selectByCategory(ifRecommend, classifyType);

        JSONArray result = new JSONArray();
        for (BookClassifyConfDAO conf : confs) {
            JSONObject jConfItem = new JSONObject();

            Long classifyId = conf.getId();
            jConfItem.put("classifyId", classifyId);
            jConfItem.put("displayType", conf.getDisplayType());
            jConfItem.put("labels", conf.getLabels());
            jConfItem.put("status", conf.getStatus());
            jConfItem.put("ifRecommend", conf.getIfRecommend());
            jConfItem.put("order", conf.getOrder());
            jConfItem.put("icon", conf.getIcon());

            List<BookClassifyRecordDAO> records = bookClassifyRecordDAOMapper.selectByClassify(classifyId);

            jConfItem.put("booksNum", records.size());
            jConfItem.put("records", new JSONArray());
            for (BookClassifyRecordDAO record : records) {
                JSONObject jItem = new JSONObject();
                Long bookId = record.getBookId();
                jItem.put("recordId", record.getId());
                jItem.put("order", record.getOrder());
                jItem.put("bookId", bookId);

                BookDAO bookDAO = bookDAOMapper.selectByPrimaryKey(bookId);
                if (bookDAO != null) {
                    jItem.put("title", bookDAO.getTitle());
                    jItem.put("author", bookDAOMapper.getAuthorName(bookId));
                    jItem.put("chaptersNum", chapterDAOMapper.totalChapters(bookId));
                    jItem.put("totalWords", chapterDAOMapper.totalWords(bookId));
                    jItem.put("discountRate", bookDAO.getDiscountRate());
                }
                jConfItem.getJSONArray("records").add(jItem);
            }

            result.add(jConfItem);
        }

        return new BaseResponse(result);
    }

    @Override
    public BaseResponse addBookClissfyRecord(Long classifyId, List<BookClissfyRecordAddDTO> body) {
        BookClassifyRecordDAO record = new BookClassifyRecordDAO();
        record.setClassifyId(classifyId);
        for (BookClissfyRecordAddDTO item : body) {
            record.setBookId(item.getBookId());
            record.setOrder(item.getOrder());
            record.setBgImg(item.getBgImg());
            bookClassifyRecordDAOMapper.insertSelective(record);
        }
        return new BaseResponse();
    }

    @Override
    public BaseResponse updateBookClissfyRecordOrder(List<BookClissfyRecordUpdateDTO> body) {
        BookClassifyRecordDAO record = new BookClassifyRecordDAO();
        for (BookClissfyRecordUpdateDTO item : body) {
            record.setId(item.getRecordId());
            record.setOrder(item.getOrder());
            bookClassifyRecordDAOMapper.updateByPrimaryKeySelective(record);
        }
        return new BaseResponse();
    }

    @Override
    public BaseResponse delBookClissfyRecord(Long recordId) {
        bookClassifyRecordDAOMapper.deleteByPrimaryKey(recordId);
        return new BaseResponse();
    }

    @Override
    public BaseResponse getBookStatisticList(BookStatisticQueryDTO param) {
        List<Long> bookIdList = param.getBookIdList();
        if (bookIdList == null) {
            bookIdList = new ArrayList<>();
        }

        if (param.getTitle() != null) {
            Map<Long, BookDAO> bookMap = bookDAOMapper.getBookMapByTitle(param.getTitle());
            for (Long bookId : bookMap.keySet()) {
                bookIdList.add(bookId);
            }
        }

        if (param.getCategory() != null) {
            List<Long> bookList = bookDAOMapper.getBookIdsByCategory(param.getCategory());
            for (Long bookId : bookList) {
                bookIdList.add(bookId);
            }
        }

        param.setBookIdList(bookIdList);

        int total = bookReadInfoDAOMapper.total(param);
        List<BookReadInfoDAO> list = bookReadInfoDAOMapper.list(param);

        JSONArray result = new JSONArray();
        for (BookReadInfoDAO item : list) {
            Long bookId = item.getBookId();
            JSONObject jItem = new JSONObject();
            jItem.put("bookId", bookId);
            jItem.put("dtdate", item.getDtdate());
            jItem.put("chapterUnlockUsers", item.getChapterUnlockUsers());
            jItem.put("chapterUnlockTimes", item.getChapterUnlockTimes());
            jItem.put("chapterUnlockKeys", item.getChapterUnlockKeys());
            jItem.put("chapterUnlockTickets", item.getChapterUnlockTickets());
            jItem.put("optionUnlockUsers", item.getOptionUnlockUsers());
            jItem.put("optionUnlockTimes", item.getOptionUnlockTimes());
            jItem.put("optionUnlockDiamonds", item.getOptionUnlockDiamonds());
            jItem.put("sumReaders", item.getSumReaders());
            jItem.put("cpt1Readers", item.getCpt1Readers());

            BookDAO bookDAO = bookDAOMapper.selectByPrimaryKey(bookId);
            if (bookDAO != null) {
                jItem.put("title", bookDAO.getTitle());
                jItem.put("cover", bookDAO.getCover());
                jItem.put("category", bookDAO.getCategory());
            }

            result.add(jItem);
        }

        return new BaseResponse(result, total);
    }

    @Override
    public JSONObject getBookStatisticListV2(String startTime, String endTime, String geoName, String channel, String bookTitle, List<Integer> bookIds, Integer offset, Integer limit) {
        Integer count = bookReadInfoDAOMapper.countBookStatisticListV2(startTime, endTime, geoName, channel, bookTitle, bookIds);
        List<BookReadInfoDAOV2> items = bookReadInfoDAOMapper.getBookStatisticListV2(startTime, endTime, geoName, channel, bookTitle, bookIds, offset, limit);
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("count", count);
        jsonObj.put("items", items);
        return jsonObj;
    }

    @Override
    public JSONObject getChapterStatisticList(String startTime, String endTime, String geoName, String channel, String bookTitle, List<Integer> bookIds, Integer chapterIndex, Integer offset, Integer limit) {
        Integer count = bookReadInfoDAOMapper.countChapterStatisticList(startTime, endTime, geoName, channel, bookTitle, bookIds, chapterIndex);
        List<ChapterInfoDAO> items = bookReadInfoDAOMapper.getChapterStatisticList(startTime, endTime, geoName, channel, bookTitle, bookIds, chapterIndex, offset, limit);
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("count", count);
        jsonObj.put("items", items);
        return jsonObj;
    }

    @Override
    public JSONObject getOptionStatisticList(String startTime, String endTime, String geoName, String channel, String bookTitle, List<Integer> bookIds, Integer chapterIndex, Integer offset, Integer limit) {
        Integer count = bookReadInfoDAOMapper.countOptionStatisticList(startTime, endTime, geoName, channel, bookTitle, bookIds, chapterIndex);
        List<OptionInfoDAO> items = bookReadInfoDAOMapper.getOptionStatisticList(startTime, endTime, geoName, channel, bookTitle, bookIds, chapterIndex, offset, limit);
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("count", count);
        jsonObj.put("items", items);
        return jsonObj;
    }
}
